package com.acme;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.acme.bankapp.domain.account.Account;
import com.acme.bankapp.domain.bank.Bank;
import com.acme.bankapp.domain.bank.BankReport;
import com.acme.bankapp.domain.client.Client;
import com.acme.bankapp.email.EmailNotificationListener;
import com.acme.bankapp.email.EmailService;
import com.acme.bankapp.exceptions.BankException;
import com.acme.bankapp.exceptions.NotEnoughFundsException;
import com.acme.bankapp.service.bank.BankDataLoaderService;
import com.acme.bankapp.service.bank.BankService;
import com.acme.bankapp.tcp.server.BankServer;

/**
 * Class used to manage bank, clients and accounts. Can print some useful
 * information.
 *
 * @author S.Zhidkov
 */

public class BankApplication {

    private static final String MESSAGE_BANK_CLIENTS = "Bank clients:";

    private static final String MESSAGE_STATISTICS = "Print bank statistics: ";
    private static final String MESSAGE_NUMBER_OF_CUSTOMERS = "Number of customers: ";
    private static final String MESSAGE_NUMBER_OF_ACCOUNTS = "Number of accounts: ";
    private static final String MESSAGE_ACCOUNTS_SORTED_BY_SUM = "Accounts sorted by sum: ";
    private static final String MESSAGE_BANK_CREDIT = "Amount of credit issued by the bank: ";
    private static final String MESSAGE_CLIENT_ACCOUNTS = "Client => Accounts: ";

    private static Bank bank;
    private static BankService bankService;
    private static BankDataLoaderService bankDataLoaderService;
    private static EmailService emailService;

    public BankApplication() {

    }

    private static void modifyBank() {
        Set<Client> clients = bank.getClients();

        for (Client client : clients) {
            Set<Account> accounts = client.getAccounts();
            for (Account account : accounts) {
                account.deposit(1000);
                try {
                    account.withdraw(450);
                } catch (NotEnoughFundsException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Prints balance of the all clients.
     */
    public static void printBalance() {
        Set<Client> clients = bank.getClients();

        System.out.println();
        System.out.println(MESSAGE_BANK_CLIENTS);
        for (Client client : clients) {
            System.out.println(client);
        }
    }

    /**
     * Method starts bank server if used command line argument '-server'
     *
     * @param bank
     */
    public static void startServer(Bank bank) {
        BankServer bankServer = new BankServer(new CommandHandler(bank));
        try {
            bankServer.run();
        } catch (BankException e) {
            e.printStackTrace();
        }
    }

    /**
     * Method used {@link BankReport} instance to print bank statistics.
     *
     * @param bank
     */
    private static void printStatistics(Bank bank) {
        BankReport bankReport = new BankReport();

        System.out.println();
        System.out.println(MESSAGE_STATISTICS);
        System.out.println(MESSAGE_NUMBER_OF_CUSTOMERS + bankReport.getNbOfCustomers(bank));
        System.out.println(MESSAGE_NUMBER_OF_ACCOUNTS + bankReport.getNbOfAccounts(bank));

        System.out.println(MESSAGE_ACCOUNTS_SORTED_BY_SUM);
        Set<Account> sortedAccounts = bankReport.getAccountsSortedBySum(bank);
        for (Account account : sortedAccounts) {
            System.out.println(account);
        }

        System.out.println(MESSAGE_BANK_CREDIT + bankReport.getBankCredit(bank));

        System.out.println(MESSAGE_CLIENT_ACCOUNTS);
        Map<Client, Set<Account>> clientAccounts = bankReport.getCustomerAccount(bank);
        Set<Client> clients = clientAccounts.keySet();

        for (Client client : clients) {
            System.out.println("Client: " + client + " => Accounts: " + clientAccounts.get(client));
        }

    }

	/**
	*
	*/
    public static void main(String[] args) throws IOException {
        // parse args
        // i use exec string:
        // "java com.acme.BankApplication -loadfeed ./files/feeds/clients.txt
        // -loadbank ./files/serialized/bank.ser -statistics"
        ArgsHandler argsHandler = new ArgsHandler(args);

        // create services
        bankDataLoaderService = new BankDataLoaderService();
        bankService = new BankService();
        emailService = new EmailService();

        // read bank instance from file
        bank = bankService.readBank(argsHandler.getPathBankLoadFrom());

        // add some listeners to the bank
        bank.addListener(new Bank.PrintClientListener());
        bank.addListener(new EmailNotificationListener(emailService));

        // read clients from feed file
        List<Client> clients = bankDataLoaderService.loadClients(argsHandler.getPathFeedFrom());

        // put clients into the bank
        bankService.addClients(bank, clients);

        if (argsHandler.isServer()) {
            startServer(bank);
        }

        // print balance on start
        printBalance();

        // modify some client accounts
        modifyBank();

        // print balance after some modifications
        printBalance();
        // and maximum to withdraw
        bankService.printMaximumAmountToWithdraw(bank);

        // close email service thread
        emailService.close();

        // remove listeners before save bank to the file
        bank.removeListeners();

        if (argsHandler.isStatistics()) {
            printStatistics(bank);
        }

        // save modified bank to the file
        bankService.saveBank(bank, argsHandler.getPathBankSaveTo());
    }

}